package com.nakamachizu.service;

import java.io.OutputStream;
import java.util.Calendar;
import java.util.Collections;
import java.util.List;

import javax.servlet.http.HttpSession;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.slim3.util.TimeZoneLocator;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

import com.google.appengine.api.datastore.GeoPt;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import com.nakamachizu.collection.LineDataSort;
import com.nakamachizu.constant.SessionKeyConstant;
import com.nakamachizu.model.LandMarkData;
import com.nakamachizu.model.LineData;
import com.nakamachizu.model.MapData;
import com.nakamachizu.model.MemberData;
import com.nakamachizu.model.session.MapDataSession;
import com.nakamachizu.model.session.MixiSessionData;
import com.nakamachizu.util.CalendarUtil;
import com.nakamachizu.util.PocketCloudStringUtil;


public class XmlGenService {
    
    public static final String XML_ROOT_ELEMENT = "mapdata";

    public Document makeXmlDocument(){
        UserService userService = UserServiceFactory.getUserService();        
        User user = userService.getCurrentUser();

        if(user == null){
            return null;
        }

        MapDataService mds = new MapDataService();
        MapData userMap = mds.getMapData(user.getEmail());
        if(userMap == null){
            return null;
        }
            
 
        return this.makeXmlDocument(userMap);
    }
    
    public Document makeXmlDocument(HttpSession session){

        MapDataSession mdses = (MapDataSession)session.getAttribute(SessionKeyConstant.MAP_DATA_SESSION_KEY());

        MapDataService mds = new MapDataService();
        MapData userMap = mds.getMapDataFromSession(mdses);
        if(userMap == null){
            return null;
        }

        return this.makeXmlDocument(userMap);
    }
    
    public Document makeXmlDocumentMixi(MixiSessionData session){
        MapDataService mds = new MapDataService();
        MapData userMap = mds.getMapDataAccessKey(session.getMapKey());
        if(userMap == null){
            return null;
        }

        return this.makeXmlDocument(userMap);
    }
    
    public Document makeXmlDocument(MapData userMap){
        if(userMap == null){
            return null;
        }
        Document document = null;
        try{
            DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docbuilder = dbfactory.newDocumentBuilder();
            document = docbuilder.newDocument();
            
            Element root = document.createElement(XML_ROOT_ELEMENT);
            document.appendChild(root);
            
            this.addNode(document, root, "key", userMap.getAccessKey());
            this.addNode(document, root, "lineType", userMap.getLineType());
            
            List<MemberData> mList = userMap.getMemberDataListRef().getModelList();
            List<LandMarkData> lList = userMap.getLandMarkDataListRef().getModelList();
            List<LineData>nList = userMap.getLineDataListRef().getModelList();
            
            if(mList != null && mList.size() > 0){
                
                for(MemberData m : mList){
                    Element member = document.createElement("member");
                    
                    Calendar cal = Calendar.getInstance(TimeZoneLocator.get());
                    cal.setTime(m.getModifyDate());
                    this.addMarker(document, root, member, KeyFactory.keyToString(m.getKey()), m.getName(), m.getComment(),
                                    Double.valueOf(m.getLatLng().getLatitude()), Double.valueOf(m.getLatLng().getLongitude()),
                                    cal, m.getPcIconName(), m.getMIconName());
                }
            }
            
            if(lList != null && lList.size() > 0){
                
                for(LandMarkData l : lList){
                    Element landmark = document.createElement("landmark");
                    Calendar cal = Calendar.getInstance(TimeZoneLocator.get());
                    cal.setTime(l.getModifyDate());
                    this.addMarker(document, root, landmark, KeyFactory.keyToString(l.getKey()), l.getName(), l.getComment(),
                        Double.valueOf(l.getLatLng().getLatitude()), Double.valueOf(l.getLatLng().getLongitude()),
                        cal, l.getPcIconName(), l.getMIconName());
    
                }
            }
            
            Collections.sort(nList, new LineDataSort());
            if(nList != null && nList.size() > 0){
                for(LineData n : nList){
                    Element line = document.createElement("line");
                    Calendar cal = Calendar.getInstance(TimeZoneLocator.get());
                    cal.setTime(n.getModifyDate());
                    this.addLine(document, root, line, KeyFactory.keyToString(n.getKey()), n.getColor(), n.getStart(), n.getEnd(), n.getGuid(), cal);
                }
            }
            
        }catch(ParserConfigurationException e){
            e.printStackTrace();
        }
        
        return document;
    }
    
    public void printXml(OutputStream os, Document document) throws TransformerException{
        
        TransformerFactory tfactory = TransformerFactory.newInstance();
        Transformer transformer = tfactory.newTransformer(); 
        transformer.transform(new DOMSource(document), new StreamResult(os)); 

    }
    
    public void addMarker(Document doc, Element parent, Element child, String id, String name, String comment, Double lat, Double lng, Calendar cal, String pcIcon, String mIcon){
        
        this.addNode(doc, child, "id", id);
        this.addNode(doc, child, "name", name);
        this.addNode(doc, child, "comment", comment);
        this.addNode(doc, child, "lat", lat.toString());
        this.addNode(doc, child, "lng", lng.toString());
        this.addNode(doc, child, "timestamp", CalendarUtil.format(cal));
        this.addNode(doc, child, "pcIcon", pcIcon);
        this.addNode(doc, child, "mIcon", mIcon);
        
        parent.appendChild(child);
    }
    
    public void addLine(Document doc, Element parent, Element child, String id, String color, GeoPt start, GeoPt end, String guid, Calendar cal){
        this.addNode(doc, child, "id", id);
        this.addNode(doc, child, "color", color.replaceAll("^0x", "#"));
        this.addNode(doc, child, "startLat", Float.toString(start.getLatitude()));
        this.addNode(doc, child, "startLng", Float.toString(start.getLongitude()));
        this.addNode(doc, child, "endLat", Float.toString(end.getLatitude()));
        this.addNode(doc, child, "endLng", Float.toString(end.getLongitude()));
        this.addNode(doc, child, "guid", guid);
        this.addNode(doc, child, "timestamp", CalendarUtil.format(cal));
        
        parent.appendChild(child);
    }
    
    public Element addNode(Document doc, Element parent, String nodeName, String nodeValue){
        Element child = doc.createElement(nodeName);
        //Text text = doc.createTextNode(nodeValue);
        
        child.setTextContent(PocketCloudStringUtil.htmlSpecialChars(nodeValue));
        parent.appendChild(child);
        
        return child;
    }
}
